iT邦幫忙

1

231解剖室:PE File Format - Part 1 頭部

  • 分享至 

  • xImage
  •  

上一篇貼文講了PE的架構,這篇會從PE的頭部下刀
深入了解DOS Header、DOS Stub
並且寫一個小parser來讀取Rich Header的資料

DOS Header (aka MS-DOS Header)

--介紹--
為一段長度為64 bytes的區段,位在PE檔案的開頭,為了讓現在的執行檔可以向下相容MS-DOS而存在。如果沒有此片段,則無法在MS-DOS上執行。

我們可以從winnt.h找到IMAGE_DOS_HEADER的定義,藉此了解他的架構
https://ithelp.ithome.com.tw/upload/images/20230121/20156936cxS5sQIBzE.png

MS-DOS的loader會根據此header來把執行檔寫入記憶體
以目前大部分的Windows系統來說,只會用到這個header裡面的兩個變數:

  • e_magic : DOS Header的signature(magic number),用來辨識此為MS-DOS執行檔,ASCII的值為MZ
  • e_lfanew : DOS Header中的最後一個變數,offset為0x3c,可以透過這個變數得知NT Headers的位置
// 透過DOS Header找到NT Headers的位置

IMAGE_DOS_HEADER *dosHeader = (IMAGE_DOS_HEADER *)fbuf; // fbuf == pointer to data read from file
IMAGE_NT_HEADERS *ntHeaders = (IMAGE_NT_HEADERS *)((size_t)dosHeader + dosHeader->e_lfanew);

PE Bear的DOS Hdr pane
https://ithelp.ithome.com.tw/upload/images/20230121/20156936LbNs4RcC8E.png

DOS Stub

--介紹--
https://ithelp.ithome.com.tw/upload/images/20230121/20156936R5RIt3mS6q.png
Microsoft的官方文件我們可以知道,DOS Stub是MS-DOS系統的執行檔,只要是合法的MS-DOS程式都可以被塞到這個區塊。而在沒有特別指定的情況下,功能為印出"This program cannot be run in MS-DOS mode."這串錯誤訊息。

--小實驗--

實際用DOSBox模擬看看在DOS系統底下執行PE執行檔

操作步驟:

  • 在C槽建一個新的資料夾 test
  • notepad執行檔複製到test資料夾裡面
  • 打開DOSBox,並把資料夾安裝到虛擬環境裡面的C槽 MOUNT C C:\test
  • 進入剛安裝的C槽並執行notepad

https://ithelp.ithome.com.tw/upload/images/20230121/20156936GzjxnhmVh5.png

執行結果應證了文件所述,但我還是很好奇他到底是怎麼運行的,於是決定來拆解他

--逆一下--

把DOS Stub分離出來會比較好做分析,底下是分離的步驟

我們先用PE Bear複製該程式片段
在讀完檔案後,點到DOS Stub區域,選取到Rich Header的offset之前,並把padding的部分刪掉
https://ithelp.ithome.com.tw/upload/images/20230121/20156936tiHZ1Ms30E.png

選取之後按右鍵複製並貼到HxD,把檔案存成dos_stub.exe
https://ithelp.ithome.com.tw/upload/images/20230121/20156936hD4QgWTPoy.png

分離好之後就可以開始分析的環節了
我決定用radare2來操作,因為他很酷

wsl環境下載完之後,就可以輸入r2 dos_stub.exe來開啟檔案。用pd印出組合語之後,會發現顯示的指令有點怪怪的,這是因為當時的處理器為16位元,與目前大家常用的64位元處理器指令集不一樣
這邊我們可以透過e asm.bits=16修正成正確的指令。
https://ithelp.ithome.com.tw/upload/images/20230121/20156936efrLvTqtnC.png

好多了,但還是有地方怪怪的,程式不小心把字串解讀成指令了。
https://ithelp.ithome.com.tw/upload/images/20230121/20156936BXy1YpvQqO.png

修正的步驟也很簡單,只要按V進入hex mode,並按c再按住shift透過hjkl或上下左右鍵進行連續選取,把字串的部分全部框起來。(藉由剛剛的小實驗,我們可以知道字串的部分是從This開始一路到最後面)
https://ithelp.ithome.com.tw/upload/images/20230121/20156936OblxnJGL8b.png

d顯示選項,可以看到有非常多的設定方式,這邊我們想要把選取的片段設成data,所以再按一次d
https://ithelp.ithome.com.tw/upload/images/20230121/20156936JvYHB2m0jX.png

設定完成後按q返回,並再試一次pd印出組合語
https://ithelp.ithome.com.tw/upload/images/20230121/20156936V6NeN1cVVv.png

根據之前的逆向經驗,簡單把程式分成三個部分

第一段是把data segment的值設定成code segment的值,可以看成ds = cs。

0000:0000    0e        push cs
0000:0000    1f        pop ds

第二段與第三段都出現int 0x21的instruction

0000:0002    ba0e00    mov dx, 0xe
0000:0005    b409      mov ah, 9
0000:0007    cd21      int 0x21
0000:0009    b8014c    mov ax, 0x4c01
0000:000c    cd21      int 0x21

根據DOS API的wiki,可以查到int 0x21是interrupt vector
透過設定ah的值來決定系統要採取什麼行動,下圖是ah數值對應到的指令表
https://ithelp.ithome.com.tw/upload/images/20230121/20156936Gcf01KXurg.png

所以我們可以知道
第二段的功能就是把在0000:000e上面的資料("This program ...")印出來
第三段的功能就是以return value = 1的狀態下終止程序

若想再深入了解DOS Stub,並嘗試更改Stub的行為,可以看這裡

Rich Header

--介紹--

介於DOS Stub與NT Headers之間的片段,用Visual Studio工具集把程式編譯成執行檔而生成的資訊,不是PE檔案格式的一部分。2018年的Olympic Destroyer病毒就是透過更改此片段來使分析過程變得困難。詳細資訊可以看這篇解析

Rich Header是由一整個chunk的加密訊息、一個singature以及一個4 bytes的XOR key所組成的。被加密的chunk由一個signature以及三個DWORD(4 bytes)大小的0作為開頭,緊接著的是一連串的DWORD pair,每個DWORD pair可以解出三組數據,分別代表使用的編譯工具(Product ID)、build ID、use count

--小實驗--

寫一個小parser當作練習,操作步驟:

  • 透過DOS Header找到NT Headers的位置
  • 假設DOS Header跟DOS Stub的長度都固定 => Rich header從0x80開始
  • 檢查Rich Header是否存在
  • 找到Rich signature,得到key的值
  • 用key把chunk分別解出來
  • 處理大小端,並把資訊印出來
// 透過DOS Header找到NT Headers的位置

IMAGE_DOS_HEADER *dosHeader = (IMAGE_DOS_HEADER *)fbuf;
IMAGE_NT_HEADERS *ntHeaders = (IMAGE_NT_HEADERS *)((size_t)dosHeader + dosHeader->e_lfanew);
// 假設DOS Header跟DOS Stub的長度都固定 => Rich header從0x80開始
// 檢查Rich Header是否存在

if (dosHeader->e_lfanew == 0x80) {
        printf("Rich Header does not exist\n");
        return;
}
// 找到Rich signature,得到key的值

memcpy(richbuf, fbuf + 0x80, richLen);
tok = strstr(richbuf, "Rich");
richOffset = tok - richbuf;
if (tok == NULL) {
        printf("Broken binary\n");
        return;
}
memcpy(key, tok + 4, 4);
// 用key把chunk分別解出來

for (int i = 0; i < richOffset; i++) {
        output[i % 8] = richbuf[i] ^ key[i % 4];
        if (i % 8 == 7) printHex(output);
}
// 處理大小端,並把資訊印出來

void printHex(char *output)
{
        unsigned char *ptr;
        unsigned char ldword[4], udword[4];
        char uformatted[16], lformatted[16];

        for (int i = 0; i < 4; i++) {
                ptr = output + i;
                ldword[4-i-1] = *ptr;
                udword[4-i-1] = *(ptr + 4);
        }

        for (int i = 0; i < 4; i++) {
                sprintf(lformatted + i * 2, "%02x", ldword[i]);
                sprintf(uformatted + i * 2, "%02x", udword[i]);
        }

        printf("%s  %s - ", lformatted, uformatted);

        if (strcmp(lformatted, "536e6144") == 0) {
                printf("%-8s", "DanS");
        } else {
                printf("%d.%d.%d",
                ldword[2] * 256 + ldword[3],
                ldword[0] * 256 + ldword[1],
                udword[2] * 256 + udword[3]);
        }

        printf("\n");

        return;
}

編譯後去parse看看notepad.exe的rich header
https://ithelp.ithome.com.tw/upload/images/20230121/20156936oVIAvEVsYE.png

用PE Bear也可以得到一樣的結果
https://ithelp.ithome.com.tw/upload/images/20230121/20156936zWtZ5Onyh2.png

richParser.c : source
(successfully built on Windows11 with gcc 11.2.0)

總結

我們提到了DOS Header的架構,並了解到這個Header對當前的Windows系統來說不是特別重要。
看完DOS Stub的官方文件之後我們決定開模擬器實際執行一次,接著用radare2把它拆了。
最後我們寫了一個parser去把Rich Header這個神秘區段的資料撈出來,並用PE Bear驗證。

下一篇文章,我們將會開始處理與現在Windows系統比較相關的NT Headers

References

0xRick's dive into the PE file format
How to end assembly correctly?
What's the purpose of PUSH CS / POP DS before a REP MOVSW?
winnt.h
radare2 meets com101
How To Run Dos Program in Windows 10 64 Bit using DOSBox Tutorial
Exploring the MS-DOS Stub


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言